//Copyright (c) 2007 Andrew Badera andrew@badera.us
//SVN repository at http://powncenet.googlecode.com/svn/

//Permission is hereby granted, free of charge, to any person
//obtaining a copy of this software and associated documentation
//files (the "Software"), to deal in the Software without
//restriction, including without limitation the rights to use,
//copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the
//Software is furnished to do so, subject to the following
//conditions:

//The above copyright notice and this permission notice shall be
//included in all copies or substantial portions of the Software.

//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
//EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
//OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
//NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
//HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
//WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
//FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
//OTHER DEALINGS IN THE SOFTWARE.

using System;
using System.Security.Cryptography;
using System.Text;

using BadUtil;

namespace BadWeb
{
    /// <summary>
    /// Class for working with WSSE authentication.
    /// </summary>
    public static class WSSE
    {
        /// <summary>
        /// Creates a nonce value for use in WSSE transactions.
        /// </summary>
        /// <returns>Nonce value for use in WSSE transactions.</returns>
        public static string GetNonce()
        {
            string noncestring = Guid.NewGuid().ToString(); //is there a better/preferred/standard way to do this?
            byte[] noncebytes = SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(noncestring));
            string noncehex = HexString.FromBytes(noncebytes);
            //maybe not necessary ...
            string shortnonce = noncehex.Substring(0, 16).ToLower();

            return shortnonce;
        }

		/// <summary>
		/// Gets a WSSE auth token.
		/// </summary>
		/// <param name="username">The username.</param>
		/// <param name="digest">The password digest.</param>
		/// <param name="shortnonce">The shortnonce value.</param>
		/// <param name="timestamp">The created timestamp.</param>
		/// <returns></returns>
		public static string GetAuthToken(string username, string digest, string nonce, string timestamp)
		{
			if (string.IsNullOrEmpty(username))
			{
				throw new ArgumentNullException("username");
			}

			if (string.IsNullOrEmpty(digest))
			{
				throw new ArgumentNullException("digest");
			}

			if (string.IsNullOrEmpty(timestamp))
			{
				throw new ArgumentNullException("timestamp");
			}

			if (string.IsNullOrEmpty(nonce))
			{
				throw new ArgumentNullException("nonce");
			}

			object[] param = new object[4];

			//service username
			param[0] = username;

			//WSSE computed digest value
			param[1] = digest;

			//WSSE nonce value
			param[2] = nonce;

			//WSSE created timestamp
			param[3] = timestamp;

			return GetAuthToken(param);
		}

        /// <summary>
        /// Gets a WSSE auth token.
        /// </summary>
        /// <param name="param">The param array containing username, digest, nonce and created values.</param>
        /// <returns>WSSE UsernameToken</returns>
        public static string GetAuthToken(object[] param)
        {
			if (param.Length < 4)
			{
				throw new ArgumentException("Param array must contain username, digest, nonce and created values.");
			}


            string authToken = string.Format("UsernameToken Username=\"{0}\", PasswordDigest=\"{1}\", Nonce=\"{2}\", Created=\"{3}\"", param);

            return authToken;
        }

        /// <summary>
        /// Computes a WSSE password digest.
        /// </summary>
        /// <param name="nonce">The nonce value.</param>
        /// <param name="created">The created value.</param>
        /// <param name="secret">The secret value.</param>
        /// <returns>Base64 string representation of WSSE password digest.</returns>
        public static string ComputePasswordDigest(string nonce, string created, string secret)
        {
            if (string.IsNullOrEmpty(nonce))
            {
                throw new ArgumentNullException("nonce");
            }

            if (string.IsNullOrEmpty(created))
            {
                throw new ArgumentNullException("created");
            }

            if (string.IsNullOrEmpty(nonce))
            {
                throw new ArgumentNullException("secret");
            }

            byte[] noncebytes = Encoding.UTF8.GetBytes(nonce);
            byte[] createdbytes = Encoding.UTF8.GetBytes(created);
            byte[] secretbytes = Encoding.UTF8.GetBytes(secret);
            byte[] allbytes = new byte[noncebytes.Length + createdbytes.Length + secretbytes.Length];

            Array.Copy(noncebytes, allbytes, noncebytes.Length);
            Array.Copy(createdbytes, 0, allbytes, noncebytes.Length, createdbytes.Length);
            Array.Copy(secretbytes, 0, allbytes, (int)(noncebytes.Length + createdbytes.Length), secretbytes.Length);

            SHA1Managed sha1 = new SHA1Managed();
            byte[] hash = sha1.ComputeHash(allbytes);

            return Convert.ToBase64String(hash);
        }
    }
}